LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

Bonbon

Go farther and see the brighter light

Mathematical_modeling

2021/9/15

2021年数学建模

比赛感想

今天抽空,记录一下刚刚过去的数学建模,我们选了C题。

在我们团队里,我主要是负责模型的求解,以及辅助模型的建立。

模型出来的很快,我们在周五就解决了第一题。然后在晚上也把后续模型都建立出来了。我在周六早上就开始写代码求解。

这里我要背锅,因为我求解得太久了!我花了整整一天都没有求解出来!走了很多弯路,也出现了很多bug,最后在周日才解出来。

最后也是很着急才发现,要将输出填入excel!我都是输出在txt上的。当时已经八点了,十点就截止了!

当时真的是十分紧张,因为输出的那个txt转excel的代码不好写,

我没有完全的信心可以在一小时内写出来,所以我真的有点害怕。

但是一个个填肯定更来不及,只能硬着头皮上了。

结果还好平常积累得多,我在半个小时就写了出来,现在回想起来还是佩服当时的冷静。

然后紧赶慢赶看了一下论文,然后就提交了。

这里非常感谢我的队友,在全程都跟我说不要紧张,非常感谢他们理解。

还是能力不够吧,从周六早上到周日晚上,连续40个小时没有休息在改代码出结果,整体上拖了团队的进度,感到非常的抱歉。


txt转换成excel的代码不普遍适用,就记录我学到的最重要的一句。

sheet.cell(row=row[i], column=col, value=value[i])

以往写入excel最麻烦的就是他的列,因为他的列式A、B、…、Z、AA、AB、…,就很麻烦。

上述这句代码就可以直接数字化列,用起来就很方便。

阅读全文

customize_domain_name

2021/9/6

定制自己的域名

用vercel托管静态博客非常方便,但是他生成的访问链接太难记住了!(而且推荐给朋友也不优雅哈哈哈)

我的博客生成的链接是:https://hexo-ebon-zeta.vercel.app/ 就真的毫无规律可循。

用了一段时间之后我就打算绑定自己的域名,好记好用而且也不贵。


首先你需要拥有一个域名,我是在阿里云上面买的

第一次使用注册啊什么的还是操作比较多的,还需要绑定邮箱和实名。

实名完还会有工作人员打电话过来,询问有没有什么需要帮助的呀,好评。


然后就可以开始挑选域名了,不同后缀是不一样的价钱的,然后前缀填自己喜欢的。

然后可以查询有没有人注册过,没有的话付款后就可以使用了。


有了域名你需要让你的域名"认识"你的博客,所以需要进行解析设置。

你需要添加两条记录,设置如下


然后在vercel里面添加你的域名就ok了


域名购买链接:https://wanwang.aliyun.com/

阅读全文

StlinkBug

2021/9/4

记录一下遇到的STLINK bug。(满屏文字警告)

回校之后,兴致满满想调试暑假写好的代码。

找了块板子,然后在keil里面配置ST-Link Debugger。

发现识别不到Stlink,显示No ST-LINK detected。

以前都是遇到No target connected,就是板子的问题。



第一次遇到这种问题然后就上网搜,每一个都说装驱动解决。也有说禁用了烧录。

然后我就照着网站,用他们提供的驱动,然后安装,结果还是没用。

为了排除是硬件上的问题,我找了几位同学,试一下烧录。

结果在他们电脑上都可以烧录。

那为什么在我电脑上就不行,配置肯定不会错啊,之前这么配置都可以的。

那么现在就可以确定是我电脑到ST-Link之间的问题。



但是我的电脑设备管理器可以显示连接ST-Link,而且没有黄色感叹号,说明没问题啊。

可是就是识别不到ST-Link(这一步我甚至为了进一步确定,在ST-LinkUpgrade.exe里面connect了一下。)

然后就开始怀疑是keil的问题,重装了最新版本keil,还是没用。

期间还遇到了keil版本和STM32F4支持包不匹配的问题,就是前者低版本不支持后者高版本。

其实只要keil版本够新就好了,支持包DFP的版本较低没关系,在keil里可以直接更新,虽然会慢一些。

低版本的keil的话甚至还要安装一个包才能使用。

算是从头到尾摸清了keil如何安装…



后续还怀疑是不是之前不小心用zadig改过ST-Link的驱动,然后改来改去还是没用。



最后发现问题是驱动不够新,这样子的结果就是设备连接显示正常但是运行不正常!!

然后就去官网下载了最新的驱动,结果就可以了。

如果最新版本驱动还不行的话,可能就需要再用zadig把驱动改成WinUSB形式的。



虽然折腾了好几天,但是对烧录、驱动等都有更多的认识,

也借此更新了keil的版本,有自动补全,而且开启界面也很漂亮。

也算是有所收获吧。



ST-Link驱动网址:https://www.st.com/en/development-tools/stsw-link009.html

DFP支持包下载地址:https://www.keil.com/dd2/pack/#!#eula-container

阅读全文

BpNet

2021/8/25

BP神经网络

隐藏层层数和神经元个数的确定

输入、输出的神经元个数很好确定,因为输入、输出对应着你的实际需求,但是隐藏层就比较难确定了。一般情况下,各个隐藏层的神经元个数相同即可。层数不宜过多,神经元添加过多的时候可以考虑增加层数。

有些可以参考的经验公式,例如有大神分享自己的经验公式

Nh=Nsα(Ni+No)N_h = \frac{N_s}{\alpha*(N_i+N_o)}

其中NiN_iNoN_oNsN_s分别代表输入层、输出层的神经元个数以及训练样本数。

α\alpha是可调值,一般在2~10。

还有可参考的一些依据,例如Nh=23(Ni+No)N_h=\frac{2}{3}(N_i+N_o)Nh<2NiN_h<2*N_i等等

目前代码里实现的是一个隐藏层,神经元个数可调。

reference:https://zhuanlan.zhihu.com/p/47519999

reference:https://zhuanlan.zhihu.com/p/100419971

数据准备

数据的存储可以使用csv文件,然后使用pandas库,读取和写入都十分方便。

注意如果单纯使用到数据的话,要看情况使得header=None。

还有就是注意维度为1的,要squeeze一下。

因为维度为1是长度为1的列表,而不是单纯一个数据。

对于输出分类,可以转换成二进制,增加输出层神经元的个数。

转换也十分方便,可以用numpy的identity生成的方阵访问下标轻松获取。

代码如下:

# 数据准备 (x,y) -> (examples_num * input_num, examples_num * output_num)
train_data = pd.read_csv('./data/trainData.csv', header=None)
train_data = shuffle(train_data)  # 打乱数据
x_train = train_data.iloc[:, 0:4].values
y_train = train_data.iloc[:, 4:5].values
y_train = np.squeeze(y_train)
y_train = np.identity(3)[y_train]  # 将标签转变为二进制

激活函数

隐藏层和输出层之前都需要激活,激活函数起到非线性的作用。

常见的有sigmoidtanh

两者比较大的差别就是前者范围是(0,1),后者是(-1,1)。

sigmoid的表达式是f(x)=11+e(x)f(x) =\frac{1}{1+e^(-x)},求导是f=f(1f)\partial{f}=f(1-f)

tanh的表达式是f(x)=exexex+exf(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}}。,求导是f=1f2\partial{f}=1-f^2

代码实现如下:

# Activation Function ("sigmoid", "tanh")
self._activationToHideName = "sigmoid"
if self._activationToHideName == "sigmoid":
    self._activationToHide = lambda i: 1 / (1 + np.exp(-i))
    self._pd_activationToHide = lambda i: i * (1 - i)  # 偏导
elif self._activationToHideName == "tanh":
    self._activationToHide = lambda i: (np.exp(i) - np.exp(-i)) / (np.exp(i) + np.exp(-i))
    self._pd_activationToHide = lambda i: 1 - i * i  # 偏导

损失函数

损失函数是来衡量网络输出与实际值的差距的。常见有均方误差(MSE)交叉熵(Cross-entropy)

假设输出是oo,实际值是yy

则MSE的表达式是 cost=12(yo)2cost =\frac{1}{2}(y-o)^2。偏导数是cost=(yo)\partial{cost}=-(y-o)

交叉熵的表达式是 cost=(log(o)y+log(1o)(1y))cost =-(log(o)*y+log(1-o)*(1-y))。偏导数是cost=yoo(1o)\partial{cost}=-\frac{y-o}{o(1-o)}

结合前面激活函数可以发现,如果使用交叉熵为损失函数和用sigmoid为激活函数,

在反向传播时偏导是要相乘的,所以可以消掉。这也是很多人把这两个结合起来一起用的原因吧。

一般选择sigmoid为输出层激活函数时,不会选择MSE,会导致权重更新过慢。

而用tanh为输出层激活函数时,不能用交叉熵,因为tanh输出包含负数。

代码实现如下:

# Loss Function ("MSE", "Cross-entropy")
self._loss_functionName = "Cross-entropy"
if self._loss_functionName == "MSE":
    self._loss_function = lambda x, y: 1 / 2 * (y - x) * (y - x)
    self._pd_loss_function = lambda x, y: -(y - x)
elif self._loss_functionName == "Cross-entropy":
    self._loss_function = lambda x, y: -(np.log(x) * y + (1 - y) * np.log(1 - x))
    self._pd_loss_function = lambda x, y: -(y - x) / (x * (1 - x))

另外代码中加入了L2正则化

所以损失函数还要加上λ2W2n\frac{\lambda}{2}\frac{\sum{W^2}}{n}

def compute_cost(self, output):
    return np.sum(self._loss_function(output, self._y_train)) / self._sample_num + \
   self._reg / 2 * (np.sum(np.square(self._w1)) + np.sum(np.square(self._w2))) / self._sample_num  # L2正则化

正向传播

正向传播分为输入层到隐藏层以及隐藏层到输出层

层与层之间也是分为线性计算激活函数两部分。

首先是输入层到隐藏层,

假设输入是ii,权值是w1w_1,偏置是b1b_1,激活函数是f1(x)f_1(x),隐藏层是hh

则从输入层到隐藏层可以表示为 h=f1(w1i+b1)h=f_1(w_1 * i +b_1)

隐藏层到输出层同理

权值是w2w_2,偏置是b2b_2,激活函数是f2(x)f_2(x),输出层是oo

o=f2(w2h+b2)o=f_2(w_2 * h + b_2)

这样就实现了整个正向传播的过程

上述的权值和输入是否需要转置是根据自己确定

值得一提的是,当输入是n个样本时,w1iw_1 * ib1b_1是不同维度的。

前者是隐藏层神经元个数*样本数,后者是 隐藏层神经元个数*1

虽然说在python代码里是可以直接相加的,但是还是要理解清楚各参数维度。

因为一旦样本数为n时,后面很多地方都需要理解分析是否求了所有样本和而需要除以n。

代码如下:

def forward(self, x):
    """前向传播"""
    a1 = np.dot(x, self._w1) + self._b1  # 维度不同的矩阵相加
    hide = self._activationToHide(a1)
    a2 = np.dot(hide, self._w2) + self._b2
    output = self._activationToOutput(a2)
    return hide, output

反向传播

反向传播其实就是求总损失对各个参数的偏导,然后用于更新参数。

求偏导运用到链式求导法则,这个需要自己推过一遍才更好理解。

注意求输入层到隐藏层时的偏导来自于输出层的很多部分,要逐一求后叠加。

代码如下:

def backward(self, hide, output):
    """反向传播"""
    # output -> hide
    d_activate2out = self._pd_loss_function(output, self._y_train) * self._pd_activationToOutput(output)
    dw2 = np.dot(hide.T, d_activate2out) / self._sample_num
    db2 = np.sum(d_activate2out, axis=0, keepdims=True) / self._sample_num

    # hide -> input
    d_activate2hide = np.dot(d_activate2out, self._w2.T) * self._pd_activationToHide(hide)
    dw1 = np.dot(self._x_train.T, d_activate2hide) / self._sample_num
    db1 = np.sum(d_activate2hide, axis=0, keepdims=True) / self._sample_num

reference:https://www.cnblogs.com/charlotte77/p/5629865.html

更新参数

更新参数用到的就是反向传播求出来的偏导

假设更新的是w1w_1,则就是求totalw1\frac{\partial{total}}{\partial{w_1}}

然后更新参数w1=w1lrtotalw1w_1 = w_1 - lr *\frac{\partial{total}}{\partial{w_1}}。其中lrlr是学习率。

学习率这里值得一提一下,在训练初期需要较高的学习率,而在训练后期则需要较低的学习率。

所以学习率应该是一个和迭代次数有关的单调递减函数。

代码如下:

self._base_lr = 0.01
self._gamma = 0.001

self._lr = self._base_lr * pow(1 + self._gamma * i, 0.75)  # 根据迭代次数更新学习率

另外代码中加入了L2正则化,所以有

dw2 += self._reg / self._sample_num * self._w2
dw1 += self._reg / self._sample_num * self._w1

训练

训练其实就是把上述的过程结合起来,

根据迭代次数不断正向传播、反向传播、更新参数、打印损失函数值。

代码如下:

def train(self):
    """训练"""
    for i in range(self._iter_num):
        hide, output = self.forward(self._x_train)
        update_data = self.backward(hide, output)

        self._lr = self._base_lr * pow(1 + self._gamma * i, 0.75)  # 根据迭代次数更新学习率
        self.update_parameter(*update_data)

        cost = self.compute_cost(output)
        if i % 1000 == 0:
            print(f"\033[31m 迭代次数{i},损失函数值{cost}\033[0m")

测试

最后是通过训练出来的参数,对测试样本进行正向传播,看看效果。

这里采用取网络输出值和实际值最大值的下标,

通过比较就可以知道相似度了

代码如下:

def test(self):
    _, test_out = self.forward(self._x_test)

    # 对比网络输出和实际值 取最大值的下标进行对比
    test_out = np.argmax(test_out, axis=1)
    y_test = np.argmax(self._y_test, axis=1)
    print("预测准确率:{:.2f}%".format(np.sum(test_out == y_test) / len(test_out) * 100))

图形界面显示

画图显示可以更直观看出数值变化,所以我就自己写了一个Display的类,输入数值即可看到随时间的变化。

有动态时间轴显示和静态时间轴显示两种方法。

前者是时间轴会不断向左移动,旧的数据会逐步往左消失掉,避免数据量太大,导致显示的比例尺不对。

后者是时间轴不会移动,旧的数据不会消失掉,用于最后观察整个过程数据的变化。

代码如下:

import matplotlib.pyplot as plt
from collections import deque
import time


class Display:
    def __init__(self):
        self._fig, self._ax = plt.subplots()
        self._start_time = time.time()

        # 随着时间轴的推移,负轴部分的数据应该舍弃 而负的数据在头部 为了提高效率选用单向队列FIFO
        self._time = deque()
        self._y = deque()
        
        self._threshold = -1.0  # 较小数舍弃阈值 影响显示画面最左侧
        self._timeline_left_parameter = 0.2  # 时间轴左移速度 影响画面往右更新

    def dynamic_timeline_show(self, data):
        # plt.ion()

        # 要显示的数据
        self._time.append(time.time() - self._start_time)
        self._y.append(data)

        # 显示前先清空上一帧
        self._ax.cla()

        # 动态设置x范围后画出
        self._ax.set_xlim(0, self._time[-1] + 10)
        self._ax.plot(self._time, self._y, c="b")

        # 时间轴左移
        for i in range(len(self._time)):
            self._time[i] -= self._timeline_left_parameter

        # 较小数舍弃
        if self._time[0] < self._threshold:
            self._time.popleft()
            self._y.popleft()

        # 延时
        plt.pause(0.1)

        # plt.ioff()

    def stationary_timeline_show(self, data):
        # plt.ion()
        
        self._time.append(time.time() - self._start_time)
        self._y.append(data)
        self._ax.plot(self._time, self._y, c="b")

        # 延时
        plt.pause(0.1)

学习感想

神经网络的学习,公式的推导还是需要自己去手写去理解,自己推导过一遍才能理解更深。

刚开始学的时候可以把每一项都写出来,也便于矩阵相乘理解。

把正向传播和反向传播都推导过一遍,理解各个层的维度变化。

然后增加到n组样本,发现其实就是改个数字,有些地方要注意除以样本数而已。

开学前的最后一篇学习笔记也写完了√

阅读全文

QuadrotorFlightPrinciple

2021/8/8

四旋翼飞行原理(Quadrotor Flight Principle)

首先四旋翼的各个飞行动作是根据四个螺旋桨产生的拉力以及力矩实现的。

以下以X型四旋翼为例。

首先安装是就要注意1、3同桨,2、4同桨。例如1、3正桨,2、4反桨。

然后就是接三相电机线,让1、3与2、4不同方向旋转。例如1、3逆时针,2、4顺时针。

如上图所示,螺旋桨旋转不仅会带来拉力,也会带来力矩,接下来我们按不同飞行动作逐个讨论。

悬停

悬停的思路是让力矩之和为0,也就是四旋翼自身不会旋转。

当四个螺旋桨同速度转动,力矩之和为0。

当这个速度满足产生的拉力等于自身重力时,四旋翼悬停。

升降

升降的思路和悬停一样,同样是让力矩之和为0,也是四个螺旋桨同速度转动,

当这个速度满足产生的拉力大于自身重力时,四旋翼上升;

当这个速度满足产生的拉力小于自身重力时,四旋翼下降。

前后

以悬停为前提,前后运动应该分为两个步骤来理解。

1.首先是前后螺旋桨不同速导致前后俯仰。

例如上图,同时同量减少1、4的转速,同时同量增加2、3的转速,会导致四旋翼向前俯仰。

向前俯仰的话四旋翼产生的升力就有了向前的水平分量,同时竖直分量减少。

2.所有螺旋桨同时增加速度以保证升力竖直分量能平衡重力,而水平分量就驱动前进。

例如上图,同时同量增加所有螺旋桨的速度,以增大升力,抵消重力。

但是我们实际操作时不能将其分为两个步骤来进行,我们应该把他们合成在一起,

例如1、4电机减少1,而2、3电机增加1导致步骤1的俯仰,同时全部电机增加0.2以平衡重力。

合成到最后也就是1、4电机减少0.8,而2、3电机增加1.2可以导致四旋翼前进。

四旋翼的后退同理。

左右

左右和前后其实类似,也是分为两个步骤来理解,先向左右翻滚,然后再同时同量增加拉力以抵消重力。

偏航(yaw)

同时同量增加1、3的转速,同时同量减少2、4的转速,

由牛三可知,

1、3(逆时针)的增速,会导致顺时针的力矩增加,

2、4(顺时针)的减速,会导致逆时针的力矩减少

综上,四旋翼将顺时针偏航。

逆时针同理。

俯仰(pitch)

俯仰在四旋翼前后运动时就有提及。同时同量增加2、3的转速,同时同量减少1、4的转速,就能向前俯仰。

向后同理。

翻滚(roll)

翻滚在四旋翼左右运动时就有提及。同时同量增加3、4的转速,同时同量减少1、2的转速,就能向右翻滚。

向左同理。

总结

以1,4之间为机头。

(其中1-0.8=0.2、1+1.2=2.2)

电机1 电机2 电机3 电机4
悬停 1 1 1 1
升降(数值相等) >1 >1 >1 >1
<1 <1 <1 <1
前后 0.2 2.2 2.2 0.2
2.2 0.2 0.2 2.2
左右 2.2 2.2 0.2 0.2
0.2 0.2 2.2 2.2
偏航(增量减量相等) 左偏 <1 >1 <1 >1
右偏 >1 <1 >1 <1
俯仰(增量减量相等) 前俯 <1 >1 >1 <1
后仰 >1 <1 <1 >1
翻滚(增量减量相等) 左翻 >1 >1 <1 <1
右翻 <1 <1 >1 >1

参考

reference:https://www.bilibili.com/video/BV1my4y1v7yC?p=4

阅读全文

kalman

2021/8/3

卡尔曼滤波(Kalman filter)

测量值

在讲卡尔曼滤波之前,我觉得必须强调的一件事就是对于测量值的理解。举个例子,比如我们测得桌子的长度是1米。我们知道误差肯定是存在的,所以这个桌子的长度可能实际上是1.01米或者0.99米。所以桌子的长度的测量值是1米,其实可以理解成桌子的长度是1米的概率最大。如果我们认为桌子的长度满足正态分布,我们也就会发现这个1米其实就是正态分布的均值,因为正态分布均值出现的概率是最高的。从概率分布函数上可以看出,桌子长度也可能是其他值,但是概率比1米要小。

思路

对于卡尔曼滤波的入门理解,我觉得还是先抛开矩阵会好一些。矩阵对于表示公式,参与计算等十分方便。但是如果你连整个算法的框架与思路都不了解,矩阵表示只会给你学习带来更大的难度。也许你记住了所有矩阵表示,但是你会发现,真正实际应用时,你会一脸懵,无从下手。边实践边学习边理解是高效的学习方法。

其实卡尔曼滤波的思路很简单,但于需要根据不同场景具体分析。它的思路其实就是根据多传感器有交集的特点,进行数据融合。什么是有交集呢?简单来讲就是你的一个信息,可以通过一个传感器直接读出,或者可以多个传感器运算得出。举个例子就是,对于你当前的位置,你可以通过GPS获得,你也可以通过你1小时前的位置结合你1小时走过的路程获得。前者就是卡尔曼滤波中的测量值,后者则是卡尔曼滤波中的预测值。

测量值我们是可以直接获得的,预测值我们是需要通过不同场景去分析的。

高斯融合分布

那么问题来了,我们有了这个预测值和测量值,这两个值我们要怎么应用,才能找到最优值呢。

还是同一个思路,这里的两个值,其实都是两个高斯分布,对应着两个均值和两个方差。测量的方差需要采样获得,预测的方差根据调试给出。因为自回归的原因,预测方差影响的是收敛速度。我们现在要把这两个高斯分布进行运算获得最优解。

看下面这个图,橙色区域代表测量值,蓝色区域代表预测值。其实实际上整副图像都有测量值和预测值,只是没有颜色的区域的概率很小,有颜色的区域可以理解成3σ\sigma​​​​​​​​​​​​​区域内。很明显看出,我们需要的最优解就是画圈的地方,也就是测量值和预测值的交集,我们需要将两个高斯分布融合,也就是相乘。

(高斯融合有详细的化简过程,嫌复杂的也可以直接看结果)

假设有两个高斯分布 测量值代表的高斯分布均值为μ1\mu_1​​​,方差为σ1\sigma_1​​​,预测值代表的高斯分布均值为μ2\mu_2​​​,方差为σ2\sigma_2​​。

则两个高斯分布的概率分布如下

f(x)=12πσ1e(xμ1)22σ12f(x) =\frac{1}{\sqrt{2\pi}\sigma_1}e^{-\frac{(x-\mu_1)^2}{2\sigma_1^2} }

g(x)=12πσ2e(xμ2)22σ22g(x) =\frac{1}{\sqrt{2\pi}\sigma_2}e^{-\frac{(x-\mu_2)^2}{2\sigma_2^2} }

将两个概率分布相乘

(当前只支持行间和行内公式渲染 所以就采用图片的方式)

所以结果是一个均值μ0\mu_0​​=μ1σ22+μ2σ12σ12+σ22\frac{\mu_1\sigma_2^2+\mu_2\sigma_1^2}{\sigma_1^2+\sigma_2^2}​​,方差σ0\sigma_0​​=σ1σ2σ12+σ22\frac{\sigma_1\sigma_2}{\sqrt{\sigma_1^2+\sigma_2^2}}​​的高斯分布乘以一个系数k0k_0​​=e(μ1μ2)22(σ12+σ22)(2π(σ12+σ22))\frac{e^{-\frac{(\mu_1-\mu_2)^2}{2(\sigma_1^2+\sigma_2^2)}}} {\sqrt{(2\pi(\sigma_1^2+\sigma_2^2))}}​​。

将结果归一化,使得总概率为1,也就是去掉这个系数k0k_0​​​​​。

对于结果这个高斯分布,我们也是需要取概率最大的值,也就是均值。所以这里的μ0\mu_0​​​也就是我们需要的最优解。

至于大家经常见到的卡尔曼增益其实就是gain=σ22σ12+σ22gain =\frac{ {\sigma_2}^2}{ {\sigma_1}^2+{\sigma_2}^2}​​​。

最优解的另一种表示方式是best=μ2+gain(μ1μ2)best =\mu_2 + gain*(\mu_1-\mu_2)​​​。其实化简一下也就是上述的μ0\mu_0​。

计算出最优值后,需要进行自回归,需要用到这里的σ0\sigma_0​​。我们称为当前值距离最优值的方差。

其实如果完全带入公式的话,应该也不会出现卡尔曼增益。但是其实μ0\mu_0σ0\sigma_0​的计算有重复的地方,个人觉得卡尔曼增益的意义就是更好地表示和计算,也可能是对其了解得还不够深。

具体例子

以雷达的一束数据为例,结合全场定位的数据,运用卡尔曼滤波获得当期最优解。

假设全场定位上一秒的数据为x0x_0y0y_0,当前的数据为x1x_1y1y_1​,

则车子在这一秒运动的Δx=x1x0\Delta x=x_1-x_0Δy=y1y0\Delta y=y_1-y_0

假设雷达上一秒的数据为s0s_0​​,则可以预测当前的雷达数据为s=(s0cosθΔx)2+(s0sinθΔy)2s'=\sqrt{(s_0\cos{\theta}-\Delta x)^2+(s_0\sin{\theta}-\Delta y)^2}​。

而当期测得的雷达数据是s1s_1,我们要把ss's1s_1进行融合。

我们先设定当前值距离最优值的方差为σ\sigma,这个值会随着自回归而收敛,不要为0即可。

假设我们给定预测方差为σ0\sigma_0​​,则我们预测的雷达的方差σ=σ2+σ02\sigma'=\sqrt{\sigma^2+\sigma_0^2}​​​。​

假设我们测量方差为σ1\sigma_1。我们现在相当于拥有两个正态分布

测量:均值s1s_1​​,方差σ1\sigma_1​​,预测:均值ss'​​,方差σ\sigma'

ok,那我们要的最优解best=s1σ2+sσ12σ12+σ2best=\frac{s_1\sigma'^2+s'\sigma_1^2}{\sigma_1^2+\sigma'^2},然后自回归,σ=σ1σσ12+σ2\sigma=\frac{\sigma_1\sigma'}{\sqrt{\sigma_1^2+\sigma'^2}}​。

当然这里用卡尔曼增益可以减少重复计算量,即

gain=σ12σ12+σ2gain =\frac{ {\sigma_1}^2}{ {\sigma_1}^2+{\sigma'}^2}​​,best=s+gain(s1s)best =s' + gain*(s_1-s')​​,σ=(1gain)σ2\sigma=\sqrt{(1-gain)*\sigma'^2}​​​。

完成√

参考

reference:https://www.zhihu.com/question/23971601/answer/770830003

reference:https://blog.csdn.net/u010720661/article/details/63253509

reference:https://blog.csdn.net/Ronnie_Hu/article/details/111378283

阅读全文

my first blog

2021/5/24

试试插入图片

markdown文章使用的图片路径和hexo博客发布时的图片路径不一致

所以我们要在编写博客的时候能看到图片而且上传后也能看到图片

我们就需要做以下配置

首先在**_config.yml文件中修改post_asset_folder:true**

然后安装hexo-asset-image 根目录执行

npm install https://github.com/CodeFalling/hexo-asset-image --save

他的作用在于你新建一篇文章之后会帮你在文章下面创建一个同名文件夹 就可以把图片放里面了

阅读全文